import java.io.DataOutputStream;
import java.io.File;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Scanner;

public class JennaKingsburyA3server implements Runnable {

	private static ArrayList<RoutingTableEntry> routingTable = new ArrayList<RoutingTableEntry>();
	private static ArrayList<NetworkNode> dvrMessageElements = new ArrayList<NetworkNode>();
	private static ArrayList<NetworkNode> neighborTable = new ArrayList<NetworkNode>();
	public static ServerSocket server;
	public static BufferedReader inFromClient;
	public static Socket connectionSocket;
	public static String dvrMessage;

	public static boolean syncBool = false;
	public static boolean exitBool = false;
	public static boolean messageBool = true;
	public static boolean receivingMessage = true;
	public static long startT2 = 0, stopT2, totalT2;

	public static void main(String[] args) throws IOException {
		int choice = -1;

		// Initializations
		readInNeighborTable();
		initializeRoutingTable();
		printRoutingTable();

		long startT1 = 0, stopT1, totalT1;
		choice = getUserInput();
		if (choice == 2) {
			new Thread(new JennaKingsburyA3server()).start();
		}

		// We will use this to store the dvrMessage that the client will send
		// us.
		String dvrMessage = null;

		// Create the server's "guidepost" socket. It should share a port with
		// the client socket.
		ServerSocket welcomeSocket = new ServerSocket(33445);

		// Set up a specialized socket for communication between the client and
		// the server.
		Socket connectionSocket = welcomeSocket.accept();

		BufferedReader inFromClient = new BufferedReader(new InputStreamReader(
				connectionSocket.getInputStream()));

		try {
			// Start of time T1
			startT1 = System.currentTimeMillis();
			startT2 = System.currentTimeMillis();
			while (receivingMessage) {
				if (messageBool) {
					messageBool = false;
					// Get the message from the client.
					dvrMessage = inFromClient.readLine();
					if (dvrMessage == null) {
						receivingMessage = false;
					} else {
						dvrMessage = dvrMessage.trim();
						System.out
								.println("The DVR Message received from the Client is: "
										+ dvrMessage);
						// Additional Processing here:
						// Take the message we just got and update our routing
						// table via the algorithm.
						System.out.println("About to process dvrMessage...");
						processDVRMessage(dvrMessage); // Puts the elements of
														// dvrMessage into
														// dvrMessageElements.
						if (choice != 2) {
							System.out.println("About to update the routing table...");
							updateRoutingTable();
							System.out.println("About to print the routing table...");
							printRoutingTable();
							System.out.println("Routing table printed.");
							messageBool = true;
						} else{
							syncBool = true;
						}
					}
				}
			}
		} catch (Exception eRef) {
			// Do nothing! Just hang in there!
		}

			if(choice == 1){
				// End of time T1
				stopT1 = System.currentTimeMillis();
				totalT1 = stopT1 - startT1;
				System.out.println("Total time T1 (in milliseconds): " + totalT1);
				System.out.println("Total time T1 (in seconds): " + (totalT1 / 1000.0));
			}
		connectionSocket.close();
		welcomeSocket.close();

	}

	// Updates the routing table based on the information in dvrMessageElements
	public static void updateRoutingTable() {
		int V;
		int D;
		int linkWeight = -999999; // Will make any errors obvious; this value
									// should be changed during execution.
		int N = -1; // Sender is referred to as "N" in the algorithm.
		int C; // Name taken from the algorithm.
		int sourceNode = neighborTable.get(0).destination;
		boolean inRoutingTable;
		// for each entry in the message
		for (int i = 0; i < dvrMessageElements.size(); i++) {
			V = dvrMessageElements.get(i).destination;
			D = dvrMessageElements.get(i).distance;

			// If we haven't already, Compute linkWeight
			if (linkWeight == -999999) {
				// C = dist + the weight of the link assigned over which the
				// dvrMessage arrived (which is dest).
				// Scour the neighbor table to find linkWeight
				for (int j = 0; j < neighborTable.size(); j++) {
					// If we're looking at the correct entry in the neighbor
					// table:
					if (neighborTable.get(j).destination == dvrMessageElements
							.get(0).destination) {
						linkWeight = neighborTable.get(j).distance;
						N = dvrMessageElements.get(0).destination;
						break;
					}// end if
				}// end for
			}// end compute linkWeight

			C = D + linkWeight;
			// Update the routing table
			inRoutingTable = false;// false until proven true;

			// Check to see if V is already in routing table.
			for (int j = 0; j < routingTable.size(); j++) {
				if (routingTable.get(j).destination == V) {
					inRoutingTable = true;
				}// end if
			}// end for
				// If the destination is NOT already covered in our routing
				// table
			if (!inRoutingTable) {
				if (i == 0) {
					RoutingTableEntry temp = new RoutingTableEntry(V, N,
							linkWeight);
					routingTable.add(temp);
					inRoutingTable = false;
				} else {
					routingTable.add(new RoutingTableEntry(V, N, C));
					inRoutingTable = false;
				}
			} else if (V != sourceNode) {
				for (int j = 0; j < routingTable.size(); j++) {
					if (routingTable.get(j).nextHop == N) {
						routingTable.get(j).distance = C;
					} else if (routingTable.get(j).distance > C) {
						routingTable.get(j).nextHop = N;
						routingTable.get(j).distance = C;
					}// end if/else if
				} // end for
			}// end if/else
		}// end for
	}// end updateRoutingTable

	public static int getUserInput() {
		int choice = -1;
		Scanner console = new Scanner(System.in);
		System.out.println("Please select an option:");
		System.out.println("1 for Option 1");
		System.out.println("2 for Option 2");
		System.out.println();
		try {
			choice = console.nextInt();
			if (choice != 1 && choice != 2) {
				throw new Exception("Invalid Input.");
			}
		} catch (Exception eRef) {
			System.out.println(eRef.getMessage());
			choice = getUserInput();
		}

		console.close();
		return choice;
	}

	public static void readInNeighborTable() throws IOException {
		File routingData = new File("WT.txt");
		Scanner input = new Scanner(routingData);
		int dest;
		int dist;

		while (input.hasNextInt()) {
			dest = input.nextInt();
			dist = input.nextInt();
			neighborTable.add(new NetworkNode(dest, dist));
		}

		input.close();
	}

	public static void initializeRoutingTable() {
		if (neighborTable.size() > 0) {
			routingTable.add(new RoutingTableEntry(
					neighborTable.get(0).destination, 0, 0));
		}
	}

	public static void printRoutingTable() {
		for (int i = 0; i < routingTable.size(); i++) {
			System.out.println(routingTable.get(i).toString());
		}
	}

	public static void processDVRMessage(String message) {
		dvrMessageElements.clear();
		Scanner input = new Scanner(message);
		int dest;
		int dist;

		while (input.hasNextInt()) {
			dest = input.nextInt();
			dist = input.nextInt();
			dvrMessageElements.add(new NetworkNode(dest, dist));
		}

		input.close();
	}
	
	@Override
	public void run() {
		while (!exitBool) {
			if (syncBool) {
				System.out.println("About to update the routing table...");
				updateRoutingTable();
				System.out.println("About to print the routing table...");
				printRoutingTable();
				System.out.println("Routing table printed.");
				syncBool = false;
				messageBool = true;
			}
			if(!receivingMessage) {
				exitBool = true;
			}
		}
		stopT2 = System.currentTimeMillis();
		totalT2 = stopT2-startT2;
		System.out.println("Total time T2 (in milliseconds): " + totalT2);
		System.out.println("Total time T2 (in seconds): " + (totalT2/1000.0));
	}
}
